import re

import bleach
import markdown
from bleach_allowlist import markdown_tags, markdown_attrs
from django import template
from django.utils.safestring import mark_safe

from bookmarks import utils
from bookmarks.models import UserProfile

register = template.Library()


@register.simple_tag(takes_context=True)
def update_query_string(context, **kwargs):
    query = context.request.GET.copy()

    # Replace query params with the ones from tag parameters
    for key in kwargs:
        query.__setitem__(key, kwargs[key])

    return query.urlencode()


@register.simple_tag(takes_context=True)
def replace_query_param(context, **kwargs):
    query = context.request.GET.copy()

    # Create query param or replace existing
    for key in kwargs:
        value = kwargs[key]
        query.__setitem__(key, value)

    return query.urlencode()


@register.filter(name="first_char")
def first_char(text):
    return text[0]


@register.filter(name="remaining_chars")
def remaining_chars(text, index):
    return text[index:]


@register.filter(name="humanize_absolute_date")
def humanize_absolute_date(value):
    if value in (None, ""):
        return ""
    return utils.humanize_absolute_date(value)


@register.filter(name="humanize_relative_date")
def humanize_relative_date(value):
    if value in (None, ""):
        return ""
    return utils.humanize_relative_date(value)


@register.tag
def htmlmin(parser, token):
    nodelist = parser.parse(("endhtmlmin",))
    parser.delete_first_token()
    return HtmlMinNode(nodelist)


class HtmlMinNode(template.Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        output = self.nodelist.render(context)

        output = re.sub(r"\s+", " ", output)

        return output


@register.simple_tag(name="markdown", takes_context=True)
def render_markdown(context, markdown_text):
    # naive approach to reusing the renderer for a single request
    # works for bookmark list for now
    if not ("markdown_renderer" in context):
        renderer = markdown.Markdown(extensions=["fenced_code", "nl2br"])
        context["markdown_renderer"] = renderer
    else:
        renderer = context["markdown_renderer"]

    as_html = renderer.convert(markdown_text)
    sanitized_html = bleach.clean(as_html, markdown_tags, markdown_attrs)
    linkified_html = bleach.linkify(sanitized_html)

    return mark_safe(linkified_html)


def append_attr(widget, attr, value):
    attrs = widget.attrs
    if attrs.get(attr):
        attrs[attr] += " " + value
    else:
        attrs[attr] = value


@register.filter("form_field")
def form_field(field, modifier_string):
    modifiers = modifier_string.split(",")
    has_errors = hasattr(field, "errors") and field.errors

    if "validation" in modifiers and has_errors:
        append_attr(field.field.widget, "aria-describedby", field.auto_id + "_error")
    if "help" in modifiers:
        append_attr(field.field.widget, "aria-describedby", field.auto_id + "_help")

    # Some assistive technologies announce a field as invalid when it has the
    # required attribute, even if the user has not interacted with the field
    # yet. Set aria-invalid false to prevent this behavior.
    if field.field.required and not has_errors:
        append_attr(field.field.widget, "aria-invalid", "false")

    return field
